package com.xiaoyu.studyworkspace.common.websocketclient;


import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiaoyu.studyworkspace.common.kapaentity.websocket.*;
import com.xiaoyu.studyworkspace.mapper.AutoPlayMapper;
import com.xiaoyu.studyworkspace.mapper.PredictionMapper;
import com.xiaoyu.studyworkspace.pojo.*;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.springframework.data.redis.core.RedisTemplate;

@Slf4j
public class KapaSocketClient extends WebSocketClient {

    private boolean isBetted = true;
    private static final ObjectMapper objectMapper = new ObjectMapper();
    private boolean isBettingFlag = false;
    private boolean flag = false;
    private int prevent = 0;
    private AutoLog autoLog;
    private AutoConfig autoConfig;
    private boolean isLogin = false;
    private int games;
    private AutoUser autoUser;
    private AutoPlayMapper autoPlayMapper;
    private RedisTemplate<String, Object> redisTemplate;
    private PredictionMapper predictionMapper;
    /**
     * 维护socketMap
     */
    public static final Map<Integer, KapaSocketClient> KAPA_SOCKET_CLIENT_MAP = new ConcurrentHashMap<>();

    public KapaSocketClient(AutoUser autoUser, AutoPlayMapper autoPlayMapper, RedisTemplate<String, Object> redisTemplate, PredictionMapper predictionMapper) throws URISyntaxException {
        super(new URI(KapaCode.WEBSOCKET));
        this.autoPlayMapper = autoPlayMapper;
        this.redisTemplate = redisTemplate;
        this.autoUser = autoUser;
        this.predictionMapper = predictionMapper;
    }


    @Override
    public void onOpen(ServerHandshake handshake) {
        log.info("websocket客户端和服务器连接成功");
        //发送登录信息
        KapaSocket kapaSocket = new KapaSocket();
        kapaSocket.setCmdCode(1);
        kapaSocket.setCmdMerge(KapaCmdMerge.LOGIN);
        LoginInfo loginInfo = new LoginInfo();
        loginInfo.setInitId(this.autoUser.getInit_id());
        loginInfo.setSessionId(this.autoUser.getSession_id());
        kapaSocket.setData(JSONUtil.toJsonStr(loginInfo).getBytes(StandardCharsets.UTF_8));
        this.send(kapaSocket.toString().getBytes());
    }

    @Override
    public void onMessage(String message) {
        log.info("websocket客户端收到消息={}", message);
    }


    @SneakyThrows
    @Override
    public void onMessage(ByteBuffer byteBuffer) {

        String toString = new String(byteBuffer.array());
        KapaSocket kapaSocket = JSONUtil.toBean(toString, KapaSocket.class);
        if (kapaSocket.getCmdMerge() == KapaCmdMerge.ROOM_INFO) {
            if (kapaSocket.getData() == null) {
                return;
            }
            if (this.isLogin && this.isBettingFlag && (this.autoConfig.getIs_skip_betting() == 1 || this.autoConfig.getIs_skip_number() == 1)) {
                String data = new String(kapaSocket.getData());
                Map<String, RoomInfo> stringRoomInfoMap = objectMapper.readValue(data, new TypeReference<Map<String, RoomInfo>>() {
                });
                ArrayList<RoomInfo> roomInfosByBetting = new ArrayList<>();
                ArrayList<RoomInfo> roomInfosByNumber = new ArrayList<>();
                for (String key : stringRoomInfoMap.keySet()) {
                    roomInfosByBetting.add(stringRoomInfoMap.get(key));
                    roomInfosByNumber.add(stringRoomInfoMap.get(key));
                }
                KapaSocket kapaSocket2 = new KapaSocket();
                kapaSocket2.setCmdCode(1);
                kapaSocket2.setCmdMerge(KapaCmdMerge.CHANGE_ROOM);
                KapaChangeRoom kapaChangeRoom = new KapaChangeRoom();
                kapaChangeRoom.setPrevRoomId(this.prevent);
                if (this.autoConfig.getIs_skip_betting() == 1) {
                    Collections.sort(roomInfosByBetting, new Comparator<RoomInfo>() {
                        @Override
                        public int compare(RoomInfo o1, RoomInfo o2) {
                            //降序
                            return (int) (o2.getBetTotal() - o1.getBetTotal());
                        }
                    });
                    Integer roomId = Integer.valueOf(roomInfosByBetting.get(this.autoConfig.getSkip_betting() - 1).getRoomId());
                    kapaChangeRoom.setNextRoomId(roomId);
                    kapaSocket2.setData(JSONUtil.toJsonStr(kapaChangeRoom).getBytes());
                    String roomName = KapaRoomID.roomName.get(roomId);
                    this.autoLog.setPrediction(roomName);
                    this.send(kapaSocket2.toString().getBytes());
                }
                if (this.autoConfig.getIs_skip_number() == 1) {
                    Collections.sort(roomInfosByNumber, new Comparator<RoomInfo>() {
                        @Override
                        public int compare(RoomInfo o1, RoomInfo o2) {
                            //降序
                            return o2.getPlayerNumber() - o1.getPlayerNumber();
                        }
                    });
                    Integer roomId = Integer.valueOf(roomInfosByNumber.get(this.autoConfig.getSkip_number() - 1).getRoomId());
                    kapaChangeRoom.setNextRoomId(roomId);
                    kapaSocket2.setData(JSONUtil.toJsonStr(kapaChangeRoom).getBytes());
                    String roomName = KapaRoomID.roomName.get(roomId);
                    this.autoLog.setPrediction(roomName);
                    this.send(kapaSocket2.toString().getBytes());
                }
            }
            //跳过
            return;
        }
        //1.登录成功
        if (kapaSocket.getCmdMerge() == KapaCmdMerge.LOGIN) {
            //接收到说明登录成功！写入数据库，正在挂机的标识，否已经准备好，修改is_prepare值。为1则准备好了
            if (kapaSocket.getData() == null) {
                log.info("登录失败");
                autoUser.setIs_auto(0);
                autoPlayMapper.updateAutoUserIsAuto(autoUser);
                this.close();
                return;
            }
            String login = new String(kapaSocket.getData(), StandardCharsets.UTF_8);
            KapaSocket kapaSocket1 = JSONUtil.toBean(login, KapaSocket.class);
            log.info("登录信息:{},{}", kapaSocket1.getUserId(), kapaSocket1.getTotalNum());
            this.autoLog = new AutoLog();
            this.autoLog.setWin_count(0);
            this.autoLog.setBalance(kapaSocket1.getTotalNum());
            this.autoLog.setProfitAndLose(0);
            this.autoLog.setUser_id(this.autoUser.getUser_id());
            this.autoLog.setContinue_fail(0);
            this.autoLog.setMultiple_reset(0);
            this.autoLog.setContinue_fail_skip(0);
            this.autoConfig = autoPlayMapper.selectAutoConfigById(this.autoUser.getUser_id());
            isLogin = true;
            return;
        }

        /**
         * 下注
         */
        if (!isBetted) {
            //标识当前本局有无下过
            if (this.isLogin && this.flag) {
                log.info("{}用户成功连接上!进入就绪状态!", this.autoUser.getUser_id());
                //数据库中查询
                this.autoConfig = autoPlayMapper.selectAutoConfigById(this.autoUser.getUser_id());
                if (stop()) return;
                this.autoLog.setGames(this.games);
                this.autoLog.setCreate_time(LocalDateTime.now());
                selectAndBetting(this.autoConfig, this.autoLog);
                //执行完修改本局已下注的标识
                isBetted = true;
                isBettingFlag = true;
            }
        }

        /**
         * 跳房信息
         */
        if (kapaSocket.getCmdMerge() == KapaCmdMerge.CHANGE_ROOM) {
            if (kapaSocket.getData() == null) {
                return;
            }
            String data = new String(kapaSocket.getData());
            KapaSocket kapaSocket1 = JSONUtil.toBean(data, KapaSocket.class);
            this.prevent = kapaSocket1.getRoomId();
            this.autoLog.setPrediction(KapaRoomID.roomName.get(kapaSocket1.getRoomId()));
            return;
        }

        /**
         * 下注服务端返回的消息
         */
        if (kapaSocket.getCmdMerge() == KapaCmdMerge.BETTING) {
            if (kapaSocket.getData() == null) {
                return;
            }
            KapaSocket kapaSocket1 = JSONUtil.toBean(new String(kapaSocket.getData()), KapaSocket.class);
            this.autoLog.setBalance(kapaSocket1.getTotalNum());
            return;
        }
        /**
         *  4被噶的房间
         */
        if (kapaSocket.getCmdMerge() == KapaCmdMerge.KILLER_ROOM) {
            if (kapaSocket.getData() == null) {
                return;
            }
            //确保每一局为0开始
            String data = new String(kapaSocket.getData());
            KapaKillerObject kapaKillerObject = JSONUtil.toBean(data, KapaKillerObject.class);
            String killerRoom = KapaRoomID.roomName.get(kapaKillerObject.getKillRoms().get(0));
            log.info("失败的房间：{}", killerRoom);
            this.autoLog.setKill_room(killerRoom);
            this.autoLog.setResult(killerRoom.equals(this.autoLog.getPrediction()) ? "失败" : "成功");
            log.info("结果:{}", this.autoLog.getResult());
            return;
        }

        /**
         *成功和失败都会返回的消息
         */
        if (kapaSocket.getCmdMerge() == KapaCmdMerge.KILLER_INFO) {
            isBettingFlag = false;
            //重置房间
            if (kapaSocket.getData() == null) {
                return;
            }
            String data = new String(kapaSocket.getData());
            KapaSocketResult kapaSocketResult = JSONUtil.toBean(data, KapaSocketResult.class);
            if (kapaSocketResult.getType() == 0) {
                if (kapaSocketResult.getBetNum() != 0) {
                    //失败
                    this.autoLog.setIncome(0.0);
                    this.autoLog.setWin_count(0);
                    this.autoLog.setContinue_fail(this.autoLog.getContinue_fail() + 1);
                    this.autoLog.setMultiple_reset(this.autoLog.getMultiple_reset() + 1);
                    this.autoLog.setContinue_fail_skip(this.autoConfig.getContinue_fail_skip());
                    //非轮空失败后是设置跳过
                    this.autoLog.setBetting(kapaSocketResult.getBetNum());
                    this.autoLog.setProfitAndLose(this.autoLog.getProfitAndLose() - this.autoLog.getBetting());
                    //重置记录失败
                    if (autoLog.getMultiple_reset() >= this.autoConfig.getMultiple_reset()) {
                        this.autoLog.setMultiple_reset(0);
                    }
                    wtriting();
                }
            }
            if (kapaSocketResult.getType() == 1) {
                if (kapaSocketResult.getBetNum() != 0) {
                    //有下注
                    this.autoLog.setBetting(kapaSocketResult.getBetNum());
                    //胜利清0
                    this.autoLog.setContinue_fail(0);
                    this.autoLog.setMultiple_reset(0);
                    this.autoLog.setWin_count(this.autoLog.getWin_count() + 1);
                    this.autoLog.setIncome(kapaSocketResult.getRevenue());
                    this.autoLog.setBetting(kapaSocketResult.getBetNum());
                    this.autoLog.setProfitAndLose(this.autoLog.getProfitAndLose() + (this.autoLog.getIncome() - this.autoLog.getBetting()));
                    wtriting();
                }
            }
            //余额不够投入,结束
            if (this.autoConfig.getSize() > this.autoLog.getBalance()) {
                //余额不足
                log.info("余额不够投入,结束！");
                autoUser.setIs_auto(0);
                autoPlayMapper.updateAutoUserIsAuto(autoUser);
            }
            return;
        }

        if (kapaSocket.getCmdMerge() == KapaCmdMerge.COUNTDOWN) {
            if (kapaSocket.getData() == null) {
                return;
            }
            //写入类属性中flag
            String countDown = new String(kapaSocket.getData());
            int countDownInt = Integer.valueOf(countDown);
            if (countDownInt > 20) {
                if (!flag) {
                    flag = true;
                }
            } else {
                if (flag) {
                    flag = false;
                }
            }
            return;
        }

    }

    private boolean stop() {
        if (this.autoLog.getContinue_fail() >= this.autoConfig.getContinue_fail()) {
            autoUser.setIs_auto(0);
            autoPlayMapper.updateAutoUserIsAuto(autoUser);
            log.info("用户{}连败停止", autoUser.getUser_id());
            return true;
        }
        if (this.autoLog.getWin_count() >= this.autoConfig.getWincount_stop()) {
            autoUser.setIs_auto(0);
            autoPlayMapper.updateAutoUserIsAuto(autoUser);
            log.info("用户{}连胜停止", autoUser.getUser_id());
            return true;
        }

        double profitAndLose = this.autoLog.getProfitAndLose();
        if (profitAndLose >= this.autoConfig.getProfit()) {
            autoUser.setIs_auto(0);
            autoPlayMapper.updateAutoUserIsAuto(autoUser);
            log.info("用户{}盈利停止", autoUser.getUser_id());
            return true;
        }
        if (profitAndLose <= -this.autoConfig.getLose()) {
            autoUser.setIs_auto(0);
            autoPlayMapper.updateAutoUserIsAuto(autoUser);
            log.info("用户{}亏本停止", autoUser.getUser_id());
            return true;
        }
        return false;
    }

    private void wtriting() {
        //下注信息结算，写入auto_log中
        autoPlayMapper.insertAutoLogByUserId(this.autoLog);
        //数据库查出所有的该用户的记录，展示100条
        List<AutoLog> autoLogs = autoPlayMapper.selectAutoLogsBySize(autoUser.getUser_id(), 100);
        //写入缓存
        redisTemplate.opsForValue().set("auto_logs" + autoUser.getUser_id(), autoLogs);
        redisTemplate.expire("auto_logs" + autoUser.getUser_id(), 1, TimeUnit.MINUTES);
    }

    private void selectAndBetting(AutoConfig autoConfig, AutoLog autoLog) throws InterruptedException {
        /**
         * 2选房间
         */
        Prediction prediction = predictionMapper.selectLastMethods(autoConfig.getPrediction_name());
        log.info("开始选房下注,算法名字:{},预测房间:{}", prediction.getMethod(), prediction.getPrediction_room());
        autoLog.setPrediction(prediction.getPrediction_room());
        KapaSocket kapaSocket = new KapaSocket();
        kapaSocket.setCmdCode(1);
        kapaSocket.setCmdMerge(KapaCmdMerge.CHANGE_ROOM);
        KapaChangeRoom kapaChangeRoom = new KapaChangeRoom();
        //房间名字获取id
        Integer roomId = KapaRoomID.roomByID.get(prediction.getPrediction_room());
        if (roomId == null) {
            //说明本局轮空
            return;
        }
        kapaChangeRoom.setNextRoomId(roomId);
        kapaChangeRoom.setPrevRoomId(0);
        kapaSocket.setData(JSONUtil.toJsonStr(kapaChangeRoom).getBytes());
        this.send(kapaSocket.toString().getBytes());

        /**
         * 3下注
         */
        //获取倍数
        int multiple = autoConfig.getMultiple();
        //进行下注
        int bettingSize = (int) (autoConfig.getSize() * Math.pow(multiple, autoLog.getMultiple_reset()));
        betting(autoLog, bettingSize);
    }

    private void betting(AutoLog autoLog, int size) {
        KapaSocket kapaSocket1 = new KapaSocket();
        kapaSocket1.setCmdCode(1);
        kapaSocket1.setCmdMerge(KapaCmdMerge.BETTING);
        KapaBetting kapaBetting = new KapaBetting();
        if (autoLog.getContinue_fail_skip() > 0) {
            autoLog.setContinue_fail_skip(autoLog.getContinue_fail_skip() - 1);
            //不进行下注,停止,输了进行停止才不下注
            return;
        }
        while (true) {
            if (size >= 1 && size < 10) {
                int num = size;
                for (int i = 0; i < num; i++) {
                    kapaBetting.setBetNum(1);
                    size = size - 1;
                    kapaSocket1.setData(JSONUtil.toJsonStr(kapaBetting).getBytes());
                    this.send(kapaSocket1.toString().getBytes());
                }
            }
            if (size >= 10) {
                int num = size / 10;
                for (int i = 0; i < num; i++) {
                    kapaBetting.setBetNum(10);
                    size = size - 10;
                    kapaSocket1.setData(JSONUtil.toJsonStr(kapaBetting).getBytes());
                    this.send(kapaSocket1.toString().getBytes());
                }
            }
            if (size <= 0) {
                break;
            }
        }
    }

    @Override
    public void onClose(int code, String reason, boolean remote) {
        //将当前id移除
        KapaSocketClient.KAPA_SOCKET_CLIENT_MAP.remove(autoUser.getUser_id());
        log.info("websocket客户端退出连接:{}", reason);
    }

    @Override
    public void onError(Exception ex) {
        ex.printStackTrace();
        log.info("websocket客户端和服务器连接发生错误={}", ex.getMessage());
    }

    public void setBetted(boolean betted) {
        isBetted = betted;
    }

    public void setGames(Integer games) {
        this.games = games;
    }

    public AutoLog getAutoLog() {
        return autoLog;
    }

    public int getPrevent() {
        return prevent;
    }

    public void setPrevent(int prevent) {
        this.prevent = prevent;
    }
}
